package com.shinbada.modules.sys.service;

import com.google.common.collect.Maps;
import com.shinbada.common.ResultCode;
import com.shinbada.common.tree.TreeLevelUtil;
import com.shinbada.common.utils.CacheUtils;
import com.shinbada.common.utils.Encodes;
import com.shinbada.common.utils.StringUtils;
import com.shinbada.core.persistence.Page;
import com.shinbada.core.security.Digests;
import com.shinbada.core.security.shiro.session.SessionDAO;
import com.shinbada.core.service.CrudService;
import com.shinbada.excel.ExcelKit;
import com.shinbada.excel.handler.DefaultExcelReadStrategyHandlerV3;
import com.shinbada.excel.modle.CheckResult;
import com.shinbada.excel.modle.ImportExcelResult;
import com.shinbada.excel.modle.StrategyCheckService;
import com.shinbada.excel.modle.UploadModle;
import com.shinbada.exception.ServiceException;
import com.shinbada.modules.sys.entity.*;
import com.shinbada.modules.sys.mapper.OfficeMapper;
import com.shinbada.modules.sys.mapper.RoleMapper;
import com.shinbada.modules.sys.mapper.UserMapper;
import com.shinbada.modules.sys.reference.UserReferenceService;
import com.shinbada.modules.sys.utils.UserUtils;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shiro.session.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 用户管理
 *
 * @author initcap
 * @version 2016-12-05
 */
@Service
@Transactional(readOnly = true)
public class UserService extends CrudService<UserMapper, User> implements StrategyCheckService<UserImport> {

    public static final int HASH_INTERATIONS = 1024;
    public static final int SALT_SIZE = 8;
    private Map<String, String> keys;
    /**
     * key   : 层级名称 / 分割
     * value : id@parent_ids
     */
    private Map<String, String> offices;
    private Map<String, String> roles;

    @Autowired
    private OfficeMapper officeMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private SessionDAO sessionDao;
    @Autowired
    private List<UserReferenceService> userReferenceServiceList;

    /**
     * 生成安全的密码，生成随机的16位salt并经过1024次 sha-1 hash
     */
    public static String entryptPassword(String plainPassword) {
        byte[] salt = Digests.generateSalt(SALT_SIZE);
        byte[] hashPassword = Digests.sha1(plainPassword.getBytes(), salt, HASH_INTERATIONS);
        return Encodes.encodeHex(salt) + Encodes.encodeHex(hashPassword);
    }

    /**
     * 验证密码
     *
     * @param plainPassword 明文密码
     * @param password      密文密码
     * @return 验证成功返回true
     */
    public static boolean validatePassword(String plainPassword, String password) {
        byte[] salt = Encodes.decodeHex(password.substring(0, 16));
        byte[] hashPassword = Digests.sha1(plainPassword.getBytes(), salt, HASH_INTERATIONS);
        return password.equals(Encodes.encodeHex(salt) + Encodes.encodeHex(hashPassword));
    }

    @Override
    public Page<User> findPage(Page<User> page, User entity) {
        if (UserUtils.getUser().isAdmin()) {
            entity.setSupper(true);
        }
        return super.findPage(page, entity);
    }

    public SessionDAO getSessionDao() {
        return sessionDao;
    }

    /**
     * 获取用户
     *
     * @param id
     * @return
     */
    @Override
    public User get(String id) {
        return UserUtils.get(id);
    }

    /**
     * 根据登录名获取用户
     *
     * @param loginName
     * @return
     */
    public User getUserByLoginName(String loginName) {
        return UserUtils.getByLoginName(loginName);
    }

    /**
     * 通过部门ID获取用户列表，仅返回用户id和name（树查询用户时用）
     *
     * @return
     */
    @SuppressWarnings("unchecked")
    public List<User> findUserByOfficeId(String officeId) {
        List<User> list = (List<User>) CacheUtils.get(UserUtils.USER_CACHE, UserUtils.USER_CACHE_LIST_BY_OFFICE_ID_ + officeId);
        if (list == null) {
            User user = new User();
            user.setOffice(new Office(officeId));
            list = mapper.findUserByOfficeId(user);
            CacheUtils.put(UserUtils.USER_CACHE, UserUtils.USER_CACHE_LIST_BY_OFFICE_ID_ + officeId, list);
        }
        return list;
    }

    public List<User> findUserByPostId(String postId) {
        User user = new User();
        user.setPost(new Post(postId));
        return mapper.findUserByPostId(user);
    }

    @Transactional(readOnly = false)
    public void saveUser(User user) {
        // 系统需要根据当前分配的部门，更新现在的公司标识
        changeCompanyIdByOfficeId(user);
        if (StringUtils.isBlank(user.getId())) {
            user.preInsert();
            saveBeforeCheck(user);
            mapper.insert(user);
        } else {
            // 清除原用户机构用户缓存
            User oldUser = mapper.get(user.getId());
            if (oldUser.getOffice() != null && oldUser.getOffice().getId() != null) {
                CacheUtils.remove(UserUtils.USER_CACHE, UserUtils.USER_CACHE_LIST_BY_OFFICE_ID_ + oldUser.getOffice().getId());
            }
            // 更新用户数据
            user.preUpdate();
            saveBeforeCheck(user);
            mapper.update(user);
        }
        if (StringUtils.isNotBlank(user.getId())) {
            // 更新用户与角色关联
            mapper.deleteUserRole(user);
            if (CollectionUtils.isNotEmpty(user.getRoleList())) {
                mapper.insertUserRole(user);
            } else {
                throw new ServiceException(ResultCode.USER_NOT_HAVE_ROLE);
            }
            // 清除用户缓存
            UserUtils.clearCache(user);
        }
    }

    private void changeCompanyIdByOfficeId(User user) {
        if (user.getOffice() != null && StringUtils.isNotBlank(user.getOffice().getId())) {
            Office office = officeMapper.get(user.getOffice().getId());
            if (office == null) {
                throw new ServiceException(ResultCode.USER_MUST_OFFICE);
            }
            String[] officeIds = office.getParentIds().split(TreeLevelUtil.SEPARATOR);
            // 获取到第一层级主键标识，就是机构标识
            user.setCompany(new Office(officeIds[1]));
        } else {
            throw new ServiceException(ResultCode.USER_MUST_OFFICE);
        }
    }

    private void saveBeforeCheck(User user) {
        if (checkLoginNameExist(user.getId(), user.getLoginName())) {
            throw new ServiceException(ResultCode.USER_LOGIN_NAME_EXIST);
        }
        if (checkNameExist(user.getId(), user.getName())) {
            throw new ServiceException(ResultCode.USER_NAME_EXIST);
        }
        if (StringUtils.isNotBlank(user.getNo()) && checkNoExist(user.getId(), user.getNo(), user.getCompany().getId())) {
            throw new ServiceException(ResultCode.USER_NO_EXIST);
        }
    }

    private boolean checkNoExist(String id, String no, String companyId) {
        return mapper.countNoExist(id, no, companyId) > 0;
    }

    public boolean checkLoginNameExist(String id, String loginName) {
        return mapper.countLoginNameExist(id, loginName) > 0;
    }

    private boolean checkNameExist(String id, String name) {
        return mapper.countNameExist(id, name) > 0;
    }

    @Transactional(readOnly = false)
    public void updateUserInfo(User user) {
        mapper.updateUserInfo(user);
        // 清除用户缓存
        UserUtils.clearCache(user);
    }

    @Transactional(readOnly = false)
    public void deleteUser(User user) {
        userReferenceServiceList.forEach(reference -> {
            if (reference.isReference(user.getId()) && UserUtils.keepReference()) {
                throw new ServiceException(ResultCode.USER_REFERENCE_CAN_NOT_DELETE, ",引用者: " + reference.getName());
            } else if (reference.isReference(user.getId()) && !UserUtils.keepReference()) {
                reference.deleteReference(user.getId());
            }
        });
        mapper.deleteUserRole(user);
        mapper.delete(user);
        // 清除用户缓存
        UserUtils.clearCache(user);
    }

    @Transactional()
    public void updatePasswordById(String id, String loginName, String newPassword) {
        User user = new User(id);
        user.setPassword(entryptPassword(newPassword));
        user.setPasswordChangeTime(new Date());
        mapper.updatePasswordById(user);
        // 清除用户缓存
        user.setLoginName(loginName);
        UserUtils.clearCache(user);
    }

    @Transactional()
    public void updateUserLoginInfo(User user) {
        // 保存上次登录信息
        user.setOldLoginIp(user.getLoginIp());
        user.setOldLoginDate(user.getLoginDate());
        // 更新本次登录信息
        user.setLoginIp(UserUtils.getSession().getHost());
        user.setLoginDate(new Date());
        mapper.updateLoginInfo(user);
    }

    /**
     * 获得活动会话
     *
     * @return
     */
    public Collection<Session> getActiveSessions() {
        return sessionDao.getActiveSessions(false);
    }

    @Transactional()
    public void updateUserDomains(String userId, String domains) {
        mapper.updateUserDomains(userId, domains);
    }

    public List<User> listByOfficeAndPostAndUser(Office office, Post post, User user) {
        return mapper.listByOfficeAndPostAndUser(office, post, user);
    }

    @Transactional()
    public void addErrInputPassword(String userId) {
        mapper.addErrInputPassword(userId);
    }

    @Transactional()
    public void disableLogin(String id, String loginFlag) {
        mapper.changeLoginFlag(id, loginFlag);
    }

    @SneakyThrows
    @Transactional(readOnly = false)
    public ImportExcelResult<UserImport> uploadExcel(MultipartFile file, UploadModle modle) {
        // 1. 准备提前数据
        readExcelInit();
        // 2. 添加数据处理器
        DefaultExcelReadStrategyHandlerV3<UserImport> excelReadHandler = new DefaultExcelReadStrategyHandlerV3<>(modle, this);
        // 3. 执行导入
        ExcelKit.$Import(UserImport.class).readXlsx(file.getInputStream(), excelReadHandler);
        // 4. 获得导入结果集
        ImportExcelResult<UserImport> importExcelResult = excelReadHandler.results();
        // 5. 批量添加成功数据
        importExcelResult.getSuccessList().forEach(userImport -> saveUser(userImport.toUser()));
        // 返回结果,成功的数据不返回了，影响响应速度
        importExcelResult.setSuccessList(null);
        return importExcelResult;
    }

    @Override
    public CheckResult readAndCheck(UserImport entity) {
        entity.getOffice().setId(offices.getOrDefault(entity.getOffice().getLevelName(), ""));
        if (StringUtils.isBlank(entity.getOffice().getId())) {
            return CheckResult.err("您指定的部门不存在: " + entity.getOffice().getLevelName());
        }
        entity.getRole().setId(roles.getOrDefault(entity.getRole().getName(), ""));
        if (StringUtils.isBlank(entity.getRole().getId())) {
            return CheckResult.err("您指定的角色不存在: " + entity.getRole().getName());
        }

        String key = entity.getLoginName();
        if (this.keys.containsKey(key)) {
            entity.setId(this.keys.get(key));
            return CheckResult.cover(key);
        }

        // 添加数据
        this.keys.put(key, "");
        return CheckResult.pass(key);
    }

    private void readExcelInit() {
        // 获取到现在数据库中的所有数据
        List<User> db = mapper.findAllList(new User());
        this.keys = Maps.newHashMapWithExpectedSize(db.size());
        List<Office> officeList = UserUtils.getOfficeList();
        this.offices = officeList.stream().collect(Collectors.toMap(Office::getLevelName, Office::getId));
        List<Role> roleList = roleMapper.findList(new Role());
        this.roles = roleList.stream().collect(Collectors.toMap(Role::getName, Role::getId));

        // 转换键配置,请根据必要条件实现，直接使用会有问题
        db.forEach(entity -> this.keys.put(entity.getLoginName(), entity.getId()));
    }

}
